using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using OpenTK;
using System.Linq;

namespace Foundry.Engine
{
	public class Node : Transform
	{
		
		public string Name {get; set; }
		
		private Node parent;
		private LinkedList<BaseObject> children =new LinkedList<BaseObject>();
		private LinkedList<Modifier> modifiers=new LinkedList<Modifier>();
		
		public IEnumerable<BaseObject> Children { get { return children; } }
		
		/// <summary>
		/// Tells if the object will survive a scene change
		/// </summary>
		public bool Resilient { get; set; }
		
		
		public IEnumerable<Node> ChildNodes { get { return from child in children where child is Node select child as Node; } }
		public IEnumerable<Modifier> Modifiers { get { return modifiers; } }
		
		public T FindChild<T>()
			where T : BaseObject
		{
			return children.FirstOrDefault( a => a is T) as T;
		}
		
		public T FindModifier<T>()
			where T : Modifier
		{
			return modifiers.FirstOrDefault(item=> item is T) as T;
		}
		
		public override int GetHashCode ()
		{
			if(Name ==null)
				return "".GetHashCode();
			else
				return Name.GetHashCode();
		}
		public Node()
		{
			modifiers = new LinkedList<Modifier>();
			children = new LinkedList<BaseObject>();			
		}
		
		public void Remove()
		{
			Parent = null;
		}
		
		/// <summary>
		/// Gets or sets the parent node of this node 
		/// </summary>
		/// <remarks>Setting this to null will effectively remove the node from the scene.</remarks>
		public Node Parent
		{
			get
			{
				return parent;
			}
			set
			{
				if(parent !=null)
					parent.RemoveChild(this);
				if(value != null)
					value.AddChild(this);
				parent = value;
				//OnParentChanged();
			}
		}
				
		#region Child functions
		internal void AddChild(BaseObject child)
		{
			child.Scene = Scene;
			
			if(child.Handle == 0)
				InitObject(child);
			if(child is Modifier)
				lock(modifiers)
					modifiers.AddLast(child as Modifier);
			else if(child is Node)
				lock(children)
					children.AddLast(child);

			lock(Scene.linear_array);
				Scene.linear_array.AddLast(child);
		}
		
		/// <summary>
		/// Dispatches a message call to all modifiers 
		/// </summary>
		/// <param name="name">
		/// Name of the message to dispatch
		/// </param>
		/// <param name="arguments">
		/// Function call arguments
		/// </param>
		public void Dispatch(string name, params object[] arguments)
		{
			int hash = name.GetHashCode();
			foreach(var m in modifiers)
				m.Dispatch(hash, arguments);
		}
		
		/// <summary>
		/// Initializes an object. This is called by the AddChild function if the objects handle is zero. 
		/// </summary>
		/// <param name="obj">
		/// A <see cref="BaseObject"/>
		/// </param>
		internal void InitObject(BaseObject obj)
		{
			var type= obj.GetType();
			var fields = type.GetFields();
			
			
			obj.Handle = Scene.GetCounter();
			
			foreach(var f in fields)
			{
				var at = f.GetCustomAttribute<ActionAttribute>();
				if(at != null && !string.IsNullOrEmpty(at.MethodName))
				{
					var m = type.GetMethod(at.MethodName, at.arguments);
					if(m != null)
						f.SetValue(obj, Delegate.CreateDelegate(f.FieldType, obj, m));
				}
			}
			
			
			// Dispatch graphics initialization function
			if(obj.InitAction != null)
				Scene.Owner.DispatchResourceCallStack( () => { obj.InitAction(); obj.initialized = true; } );
		}
		
		internal void RemoveChild(BaseObject child)
		{
			if(child is Modifier)
				lock(modifiers)
					modifiers.Remove(child as Modifier);
			else
				lock(children)
					children.Remove(child);
			
			lock(Scene.linear_array)
				Scene.linear_array.Remove(child);
		}
		
		public bool ContainsChild(BaseObject child)
		{
			return children.Contains(child) || modifiers.Contains(child as Modifier);
		}
		
		#endregion
	}
}

